local skynet = require "skynet.manager"
local cjson = require "cjson"
local util  = require "util"
local utils = require "utils"
local reason = require "reason"
local game_def = require "game_def"
local constant = require "constant"
local server_config = dofile(skynet.getenv("server_config"))

local table_insert = table.insert
local table_remove = table.remove
local table_unpack = table.unpack
local string_format = string.format
local tonumber = tonumber
local pcall = pcall
local debug_traceback = debug.traceback

local redis_pool
local sync_uids = {}

local CMD = {}
function CMD.start()
    local redis_conf = {
        host = server_config.redis_conf.host,
        port = tonumber(server_config.redis_conf.port),
        db = tonumber(server_config.redis_conf.db),
        auth = server_config.redis_conf.auth,
    }

    redis_pool = require("redis_pool").new(redis_conf)

    skynet.retpack(true)
end

local function make_card_id(sign,number)
    return sign * 100 + (number or 3)
end

local function make_player_act_key(uid)
    return "data_" .. uid
end

--------------------------------并发控制相关[begin]-------------------------------
local function sync_run_by_uid(uid,f,...)
    local waiting_list = sync_uids[uid]
    print("sync_uids===========",uid,tostring_r(waiting_list))
    if waiting_list == nil then
        sync_uids[uid] = true
    else
        if waiting_list == true then
            waiting_list = {}
            sync_uids[uid] = waiting_list
        end
        local co = coroutine.running()
        table_insert(waiting_list,co)
        skynet.wait(co) --等待唤醒
    end

    local conn = redis_pool:get_conn_from_pool()
    local ok,ret = xpcall(f,debug.traceback,uid,conn,...)
    redis_pool:putback_to_pool(conn)
    if not ok then errlog(ret) end

    --再接着调度
    local waiting_list = sync_uids[uid]
    if waiting_list == true or #waiting_list == 0 then
        sync_uids[uid] = nil
        return
    end    

    local co = table_remove(waiting_list,1)
    skynet.wakeup(co)
end

-----------------------------------协议-----------------------------------------
local function make_act_key(act_id)
    return string.format('act_data_%d',act_id)
end

local function make_rank_act_key(act_id,issue)
    return string.format('actrank_%d_%d',act_id,issue)
end

local function make_rank_act_time_key(act_id)
    return string.format('actrank_ltime_%d',act_id)
end

local function check_activity_timeout(act_id,conn,expire_time)
    print("check_activity_timeout",act_id,expire_time)
    local key = make_act_key(act_id)
    local now = util.get_now_time()
    local time = tonumber(conn:hget(key,'expire_time') or 0) 
    if time < now then --清除数据
        print("clear_act_data=========",time,now)
        conn:del(key)
        conn:hset(key,'expire_time',expire_time)
    end

    skynet.retpack(true)
end

local function check_rank_activity_timeout(act_id,conn,issue)
    print("check_rank_activity_timeout",act_id,issue)
    local key = make_rank_act_key(act_id,issue)
    local issue_num = conn:hget(key,'issue_num') or ''
    if issue_num ~= issue then --清除数据
        print("clear_rankact_data=========",issue_num,issue)
        conn:del(key)
        conn:hset(key,'issue_num',issue)
    end

    skynet.retpack(true)
end

local function act_add_play_times(uid,conn,act_id,play_times)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end
    user_data.play_times = (user_data.play_times or 0) + play_times
    conn:hset(key,uid,cjson.encode(user_data))

    skynet.retpack(true)
end

local function act_add_login_day(uid,conn,act_id,day_count)
    local key = make_act_key(act_id)
     print("act_add_login_day=========",uid,conn,act_id,day_count,key)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end
    user_data.login_day_count = (user_data.login_day_count or 0) + day_count
    conn:hset(key,uid,cjson.encode(user_data))

    skynet.retpack(true)
end

local function act_add_day_online_time(uid,conn,act_id,online_seconds)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end
    user_data.day_online_time = (user_data.day_online_time or 0) + online_seconds
    conn:hset(key,uid,cjson.encode(user_data))

    skynet.retpack(true)
end

local function take_task_act_award(uid,conn,act_id,task_type,target_process,continue_time)
    print("take_task_act_award===============",uid,act_id,task_type,target_process,continue_time)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end

    local changed = false
    if task_type == game_def.ACT_TASK_PLAY_COUNT then
        --玩家玩牌次数小于要领取的目标值或者已经领取
        if (user_data.play_times or 0) < target_process or (user_data.play_times_map or {})[target_process] then
            return skynet.retpack(false)
        end
        user_data.play_times_map = user_data.play_times_map or {}
        user_data.play_times_map[tostring(target_process)] = true
        changed = true
    elseif task_type == game_def.ACT_TASK_LOGIN_DAY then
        --玩家登录天数小于要领取的目标值或者已经领取
        if (user_data.login_day_count or 0) < target_process or (user_data.login_day_map or {})[target_process] then
            return skynet.retpack(false)
        end
        user_data.login_day_map = user_data.login_day_map or {}
        user_data.login_day_map[tostring(target_process)] = true
        changed = true
    elseif task_type == game_def.ACT_TASK_ONLINE_TIME then
        --玩家登录天数小于要领取的目标值或者已经领取
        print("task_type================1111")
        if (user_data.day_online_time or 0) + continue_time < target_process or (user_data.online_time_map or {})[target_process] then
            return skynet.retpack(false)
        end
        print("task_type================222")
        user_data.online_time_map = user_data.online_time_map or {}
        user_data.online_time_map[tostring(target_process)] = true
        changed = true
    end

    if not changed then
        return skynet.retpack(false)
    end
    conn:hset(key,uid,cjson.encode(user_data))

    skynet.retpack(true)
end

local function take_first_charge_award(uid,conn,act_id,target_process)
    print("take_task_act_award===============",uid,act_id,task_type,target_process,continue_time)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end

    if user_data.charge_state ~= 1 or user_data.taken_status == 1 or user_data.cur_process < target_process then
        print("failed to get award",tostring_r(user_data))
        return skynet.retpack(false)
    end

    user_data.taken_status = 1
    conn:hset(key,uid,cjson.encode(user_data))

    R().basesvr({key=uid}):send('.msg_handler','update_first_charge_status',uid,1)
    skynet.retpack(true)    
end

local function get_player_activity_data(uid,conn,act_id)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end

    skynet.retpack(user_data)
end

local function act_add_day_coins(uid,conn,act_id,coins,issue)
    print("act_add_day_coins===111",uid,act_id,coins,issue)
    local key = make_rank_act_key(act_id,issue)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end

    user_data.today_coins = (user_data.today_coins or 0) + coins
    ret_coins = user_data.today_coins

    print("act_add_day_coins===222",key,tostring_r(user_data),uid,cjson.encode(user_data))

    conn:hset(key,uid,cjson.encode(user_data))
    skynet.retpack(ret_coins)
end

local function get_player_rankact_data(uid,conn,act_id,issue)
    local key = make_rank_act_key(act_id,issue)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end

    skynet.retpack(user_data.today_coins or 0)
end

local function set_actrank_give_award(act_id,conn,issue)
    print("set_actrank_give_award===111")
    local key = make_rank_act_key(act_id,issue)
    local issue_num = conn:hget(key,"issue_num")
    if not issue_num then
        errlog("set_actrank_give_award_error",act_id,issue)
        return
    end
    conn:hset(key,"give_award",true)
    print("set_actrank_give_award===222")
    
    skynet.retpack(true)
end

local function get_actrank_give_award(act_id,conn,issue)
    local key = make_rank_act_key(act_id,issue)
    local give_award = conn:hget(key,"give_award")
    skynet.retpack(give_award)
end

local function get_actrank_lat_time(act_id,conn)
    local key = make_rank_act_time_key(act_id)
    local time = conn:get(key)
    skynet.retpack(time)
end

local function set_actrank_lat_time(act_id,conn,time)
    print("set_actrank_lat_time====",act_id)
    local key = make_rank_act_time_key(act_id)
    conn:set(key,time)
end

local function del_actrank_lat_time(act_id,conn)
    print("del_actrank_lat_time====",act_id)
    local key = make_rank_act_time_key(act_id)
    conn:del(key)
end

local function player_finish_first_charge(uid,conn,act_id)
    local key = make_act_key(act_id)
    local data = {
        charge_state = 1,
        cur_process = 0,
        taken_status = 0,
    }
    conn:hset(key,uid,cjson.encode(data))
end

local function act_add_first_charge_process(uid,conn,act_id,coins)
    local key = make_act_key(act_id)
    local user_data = conn:hget(key,uid)
    if not user_data then
        user_data = {}
    else
        user_data = cjson.decode(user_data)
    end
    if not user_data.charge_state or user_data.charge_state ~= 1 then
        return skynet.retpack(false)
    end

    user_data.cur_process = user_data.cur_process + coins
    conn:hset(key,uid,cjson.encode(user_data))
    skynet.retpack(true)
end

---------------------------------------------------------------------------
function CMD.check_activity_timeout(act_id,...) sync_run_by_uid(act_id,check_activity_timeout,...) end
function CMD.check_rank_activity_timeout(act_id,...) sync_run_by_uid(act_id,check_rank_activity_timeout,...) end
function CMD.set_actrank_give_award(act_id,...) sync_run_by_uid(act_id,set_actrank_give_award,...) end
function CMD.get_actrank_give_award(act_id,...) sync_run_by_uid(act_id,get_actrank_give_award,...) end
function CMD.get_actrank_lat_time(act_id,...) sync_run_by_uid(act_id,get_actrank_lat_time,...) end
function CMD.set_actrank_lat_time(act_id,...) sync_run_by_uid(act_id,set_actrank_lat_time,...) end
function CMD.del_actrank_lat_time(act_id,...) sync_run_by_uid(act_id,del_actrank_lat_time,...) end


function CMD.act_add_play_times(uid,...) sync_run_by_uid(uid,act_add_play_times,...) end
function CMD.act_add_login_day(uid,...) sync_run_by_uid(uid,act_add_login_day,...) end
function CMD.act_add_day_online_time(uid,...) sync_run_by_uid(uid,act_add_day_online_time,...) end
function CMD.take_task_act_award(uid,...) sync_run_by_uid(uid,take_task_act_award,...) end
function CMD.get_player_activity_data(uid,...) sync_run_by_uid(uid,get_player_activity_data,...) end
function CMD.act_add_day_coins(uid,...) sync_run_by_uid(uid,act_add_day_coins,...) end
function CMD.get_player_rankact_data(uid,...) sync_run_by_uid(uid,get_player_rankact_data,...) end

function CMD.player_finish_first_charge(uid,...) sync_run_by_uid(uid,player_finish_first_charge,...) end
function CMD.act_add_first_charge_process(uid,...) sync_run_by_uid(uid,act_add_first_charge_process,...) end
function CMD.take_first_charge_award(uid,...) sync_run_by_uid(uid,take_first_charge_award,...) end
---------------------------------------------------------------------------

skynet.start(function()
    skynet.dispatch("lua",function(_,_,action,...)
        print('============act_data_interce got params==============......',action,...)
        CMD[action](...)
    end)
end)